#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>

#include <iostream>
#include <stdexcept>
#include <cstdlib>
#include <vector>
#include <cstring>
#include <vulkan/vulkan_raii.hpp>
#include <vulkan/vk_platform.h>

const uint32_t WIDTH = 800;
const uint32_t HEIGHT = 600;

class HelloTriangleApplication
{
public:
    void run()
    {
        initWindow();
        initVulkan();
        mainLoop();
        cleanup();
    }

private:
    GLFWwindow *window;
    vk::raii::Context context;
    vk::raii::Instance instance = nullptr;

    void initWindow()
    {
        glfwInit();
        // 告诉glfw不要使用后续调用来创建OPENGL上下文
        glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
        // 禁用resize
        glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
        // 初始化窗口
        window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
    }

    void initVulkan()
    {
        createInstance();
    }

    void mainLoop()
    {
        // 一直运行直到窗口关闭或者发生错误
        while (!glfwWindowShouldClose(window))
        {
            // 处理窗口事件
            glfwPollEvents();
        }
    }

    void cleanup()
    {
        // 销毁窗口
        glfwDestroyWindow(window);
        // 终止GLFW库
        glfwTerminate();
    }

    void createInstance()
    {
        // 创建Vulkan实例
        vk::ApplicationInfo appInfo{};
        appInfo.pApplicationName = "Hello Triangle";
        appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
        appInfo.pEngineName = "No Engine";
        appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
        appInfo.apiVersion = VK_API_VERSION_1_4;

        // Get the required instance extensions from GLFW.
        uint32_t glfwExtensionCount = 0;
        auto glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);

        // Check if the required GLFW extensions are supported by the Vulkan implementation.
        auto extensionProperties = context.enumerateInstanceExtensionProperties();
        for (uint32_t i = 0; i < glfwExtensionCount; ++i)
        {
            // debug print
            std::cout << "GLFW extension: " << glfwExtensions[i] << std::endl;
            // debug print extensionProperties
            for (auto const &extensionProperty : extensionProperties)
            {
                std::cout << "Vulkan extension: " << extensionProperty.extensionName << std::endl;
            }
            // Check if the required GLFW extension is supported by the Vulkan implementation.
            if (std::ranges::none_of(extensionProperties,
                                     [glfwExtension = glfwExtensions[i]](auto const &extensionProperty)
                                     { return strcmp(extensionProperty.extensionName, glfwExtension) == 0; }))
            {
                throw std::runtime_error("Required GLFW extension not supported: " + std::string(glfwExtensions[i]));
            }
        }

        vk::InstanceCreateInfo createInfo{};
        // 告诉Vulkan驱动程序我们需要的全局扩展和验证层
        createInfo.sType = vk::StructureType::eInstanceCreateInfo;
        createInfo.pApplicationInfo = &appInfo;

        // 准备扩展列表，在macOS上添加portability支持
        std::vector<const char *> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);

#ifdef __APPLE__
        // macOS需要portability扩展来支持MoltenVK
        extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
        createInfo.flags = static_cast<vk::InstanceCreateFlags>(VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR);
#endif

        createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
        createInfo.ppEnabledExtensionNames = extensions.data();

        // 创建Vulkan实例,如果出现错误则打印错误信息
        try
        {
            instance = vk::raii::Instance(context, createInfo);
            std::cout << "Vulkan实例创建成功!" << std::endl;
        }
        catch (const vk::SystemError &err)
        {
            std::cerr << "Vulkan实例创建失败: " << err.what() << std::endl;
            throw;
        }
    }
};

int main()
{
    try
    {
        HelloTriangleApplication app;
        app.run();
    }
    catch (const std::exception &e)
    {
        std::cerr << e.what() << std::endl;
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}